package com.leesche.netdoll.communication;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.leesche.framework.core.codec.HexUtil;
import com.leesche.framework.core.concurrent.ThreadUtil;
import com.leesche.framework.core.lang.StringUtil;
import com.leesche.framework.platform.redis.RedisManager;
import com.leesche.framework.platform.redis.StringCommand;
import com.leesche.netdoll.communication.Settings.RedisModule;
import com.leesche.netdoll.communication.event.MessageEventDispatcher;
import com.leesche.netdoll.communication.protocol.FrameType;
import com.leesche.netdoll.communication.protocol.IllegalChannelMessageException;
import com.leesche.netdoll.communication.protocol.MessageType;
import com.leesche.netdoll.communication.protocol.MessageUtil;
import com.leesche.netdoll.communication.protocol.SimpleMessage;
import com.leesche.netdoll.communication.protocol.StatusMessage;
import com.leesche.netdoll.communication.protocol.encoder.CommandManager;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;


public class ReceiveHandler extends ChannelInboundHandlerAdapter {

	private static final Logger logger = LogManager.getLogger(ReceiveHandler.class);
	private static final Logger inLogger = LogManager.getLogger("server-inbound-logger");
	private static final Logger outLogger = LogManager.getLogger("server-outbound-logger");
	private static final Logger rawLogger = LogManager.getLogger("server-raw-logger");

	private final static StringCommand redis = RedisManager.custom().getStringCommand(RedisModule.TERMINAL);

	private static HashMap<String,List<String>> deviceIdMap = new HashMap<String,List<String>>();
	public HashMap<String,List<String>> getDeviceIdMap(){
		return deviceIdMap;
	}
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		logger.info(ctx.channel().remoteAddress() + " connected...");

		ThreadUtil.sleep(1000);

		logger.info("sending handshake message to {}...", ctx.channel().remoteAddress());

		byte[] cmd = CommandManager.newInstance().command("ID", FrameType.START, 0);

		ChannelFuture f = ctx.writeAndFlush(Unpooled.wrappedBuffer(cmd));

		f.addListener(new ChannelFutureListener() {

			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				outLogger.info(Logs.getMsg(ctx.channel().remoteAddress(), null, HexUtil.encodeHexString(cmd)));
				logger.info("sending handshake message to {}...OK", ctx.channel().remoteAddress());
			}
		});
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		if (ctx.channel() != null) {
			String deviceId = ctx.channel().attr(Session.KEY_DEVICE_ID).get();
			if (StringUtil.isNotBlank(deviceId) && Session.session().contains(deviceId)) {
				closeChannel(ctx, "channelInactive");
			}
		}
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

		String deviceId = ctx.channel().attr(Session.KEY_DEVICE_ID).get();
		String gameId = ctx.channel().attr(Session.KEY_GAME_ID).get();

		if(deviceIdMap.get(deviceId)==null) {
			deviceIdMap.put(deviceId, new ArrayList<String>());
		}else {
			deviceIdMap.get(deviceId).add(gameId+"-"+(new Date()).getTime());
		}
		
		byte[] inData = null;
		ByteBuf buf = (ByteBuf) msg;

		try {

			if (buf != null && buf.readableBytes() > 0) {
				inData = new byte[buf.readableBytes()];
				buf.readBytes(inData);

				rawLogger.info(Logs.getMsg(ctx.channel().remoteAddress(), HexUtil.encodeHexString(inData)));

				SimpleMessage sm = MessageUtil.decode(deviceId, inData);

				if (sm != null) {
					deviceId = sm.getDeviceId();

					inLogger.info(Logs.getMsg(ctx.channel().remoteAddress(), deviceId, HexUtil.encodeHexString(inData)));

					if (sm.getCode() == MessageType.RSP_ID.getCode() && StringUtil.isNotBlank(deviceId)) {
						ctx.channel().attr(Session.KEY_DEVICE_ID).set(sm.getDeviceId());
						ctx.channel().attr(Session.KEY_GAME_ID).set(StringUtil.EMPTY);
						Session.session().add(ctx.channel());
						logger.info("device {} connected.", deviceId);

					} else {
						reply(sm, ctx);
						if (sm.getCode() != MessageType.HEARTBEAT.getCode()) {
							sm.setGameId(gameId);
							MessageEventDispatcher.dispatcher().dispatch(sm);
							//添加
							if(sm.getType()==0) {
								long qty = ((StatusMessage)sm).getQty();
								logger.info("获得的得分:",qty);
								redis.set(gameId+"terminal", String.valueOf(qty));
							}
						}
					}
				}
			}
		} finally {
			ReferenceCountUtil.release(msg);
		}

	}

	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		if (evt instanceof IdleStateEvent) {
			if (((IdleStateEvent) evt).state() == IdleState.ALL_IDLE) {
				String deviceId = ctx.channel().attr(Session.KEY_DEVICE_ID).get();
				if (StringUtil.isNotBlank(deviceId)) {
					logger.info("device {} timeout.", deviceId);
					closeChannel(ctx, "userEventTriggered");
				} else {
					logger.info("close empty channel {}", ctx.channel().remoteAddress());
					ctx.close();
				}
			}
		} else {
			super.userEventTriggered(ctx, evt);
		}
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

		if (cause instanceof IllegalChannelMessageException) {

			logger.error("illegal inbound from '{}', cause {}", ctx.channel().remoteAddress(), cause);

			ctx.close();

		} else {
			logger.catching(cause);
		}

	}

	private static void closeChannel(ChannelHandlerContext ctx, String closeBy) {
		try {
			logger.info("close channel {} by {}.", ctx.channel().attr(Session.KEY_DEVICE_ID).get(), closeBy);
		} catch (Exception e) {
			logger.catching(e);
		}
		Session.session().remove(ctx.channel(), closeBy);
		ctx.close();
	}

	private static void reply(final SimpleMessage sm, final ChannelHandlerContext ctx) {
		if (sm.getType() != 0x00)
			return;

		byte[] reply = CommandManager.newInstance().command("COMMON_RSP", sm.getCode(), FrameType.ACCEPT, Session.session().getSequece(sm.getDeviceId()));

		ChannelFuture f = ctx.writeAndFlush(Unpooled.wrappedBuffer(reply));

		f.addListener(new ChannelFutureListener() {

			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				outLogger.info(Logs.getMsg(ctx.channel().remoteAddress(), sm.getDeviceId(), HexUtil.encodeHexString(reply)));
			}
		});
	}
}
